package wallet

import (
	"bytes"
	"crypto/ecdsa"
	"crypto/elliptic"
	"encoding/gob"
	"errors"
	"github.com/boltdb/bolt"
	"publicChainSystem/tools"
)

/**
 *@author:zhengyilong
 *@email:1797344574@qq.com
 *@phone:13479394730
 *@DateTime:2022/5/30 9:47
 **/

const PRIVATE_BUCKET = "private_bucket"

//用于封装若干个功能，比如生成地址，地址校验，保存私钥，查看私钥
type Wallet struct {
	DB *bolt.DB
}

//实例化一个wallet对象
func NewWallet(db *bolt.DB) (*Wallet,error) {
	if db == nil {
		return nil,errors.New("db错误")
	}

	//创建出来一个桶
	err := db.Update(func(tx *bolt.Tx) error {
		//先判断是否有桶，无则创建，有则直接返回
		bk := tx.Bucket([]byte(PRIVATE_BUCKET))
		if bk == nil {
			//没有桶
			_, err := tx.CreateBucket([]byte(PRIVATE_BUCKET))
			if err != nil {
				return err
			}
		}
		return nil
	})
	if err != nil {
		return nil, err
	}

	return &Wallet{db},nil
}

//创建地址和私钥，并返回
func (w *Wallet) NewAddress() (string,*ecdsa.PrivateKey,error) {
	//获取公钥
	pri, pub, err := NewPubKeys()
	if err != nil {
		return "",nil,err
	}

	//对公钥进行类型转换，序列化
	pub_byte := elliptic.Marshal(pub.Curve, pub.X, pub.Y)

	//对公钥进行sha256计算
	pub_sha256 := tools.GetHash(pub_byte)

	//进行ripmed160计算	20个字节，160位
	pubhash := tools.Ripemd160(pub_sha256)

	//将版本号和pubhash进行拼接
	ver_pubhash := append([]byte{VERSION}, pubhash...)

	//双重sha256
	first_hash := tools.GetHash(ver_pubhash)
	second_hash := tools.GetHash(first_hash)

	//取前四个字节为校验码
	check := second_hash[:4]

	//将check拼接到ver_pubhash后面
	ver_pubhash_check := append(ver_pubhash,check...)

	//对最后的结果进行base58编码
	address := tools.Encode(ver_pubhash_check)

	return address,pri,nil
}

//保存地址和私钥到桶中
func (w *Wallet)SavePrivateKey(address string,pri *ecdsa.PrivateKey) error{
	//将地址和私钥以键值对的方式存入桶中
	db := w.DB
	err := db.Update(func(tx *bolt.Tx) error {
		bk := tx.Bucket([]byte(PRIVATE_BUCKET))
		if bk == nil {
			return errors.New("桶不存在")
		}
		//序列化，把私钥转为字节切片
		pribyte, err := tools.Serialize(pri)
		if err != nil {
			return err
		}

		err = bk.Put([]byte(address), pribyte)
		if err != nil {
			return err
		}
		return nil
	})
	return err
}

//用来获取私钥信息。真实的保存比特币地址是先对私钥进行AES加密，保存加密信息
func (w *Wallet)ShowPrivateKey(address string) (ecdsa.PrivateKey,error) {
	db := w.DB
	var privatekey ecdsa.PrivateKey

	err := db.View(func(tx *bolt.Tx) error {
		bk := tx.Bucket([]byte(PRIVATE_BUCKET))
		if bk == nil {
			return nil
		}

		priBytes := bk.Get([]byte(address))

		de := gob.NewDecoder(bytes.NewBuffer(priBytes))
		//注册类型
		gob.Register(elliptic.P256())
		err := de.Decode(&privatekey)
		if err != nil {
			return err
		}
		return nil
	})
	return privatekey,err
}

//校验地址
func (w *Wallet) Checkaddr(address string) bool {
	if len(address) != 34 {
		return false
	}
	//1、使用base58算法对给定的字符串进行解码操作，得到一个解码序列
	decodeAddress := tools.Decode(address)

	//2、在得到的解码序列中取后四个字节，命名为check
	check := decodeAddress[len(decodeAddress)-4:len(decodeAddress)]

	//3、取第二步中剩余的序列，进行双重hash计算，并得到校验码
	ver_pub := decodeAddress[0:len(decodeAddress)-4]
	hash := tools.GetHash(ver_pub)
	hash2 := tools.GetHash(hash)
	verify_check := hash2[:4]

	//4、将第二步中的check与第四步中的verify_check进行比较，判断
	isValid := bytes.Compare(check, verify_check)
	return isValid == 0
}